package com.lsh.oms.core.service.status.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lsh.base.common.exception.BusinessException;
import com.lsh.oms.api.model.status.FreightDto;
import com.lsh.oms.core.constant.Constants;
import com.lsh.oms.core.enums.EC;
import com.lsh.oms.core.enums.OrderStatus;
import com.lsh.oms.core.enums.YN;
import com.lsh.oms.core.model.http.HttpMessage;
import com.lsh.oms.core.model.order.OrderHead;
import com.lsh.oms.core.proxy.handler.AsyncTaskHandler;
import com.lsh.oms.core.proxy.service.EsProxy;
import com.lsh.oms.core.proxy.service.TmsProxy;
import com.lsh.oms.core.service.order.OrderQueryService;
import com.lsh.oms.core.service.order.OrderUpdateService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author peter
 */
@Component
@Slf4j
public class ModifyInfoService {

    private final static String DELIVERY_TIME_MEMO = "delivery_time_memo";

    private final static String TRANS_LIMIT = "trans_limit";

    private final static String WHEELBARROW = "wheelbarrow";

    private static Logger logger = LoggerFactory.getLogger(ModifyInfoService.class);

    @Autowired
    private OrderQueryService orderQueryService;

    @Autowired
    private OrderUpdateService orderUpdateService;

    @Autowired
    private TmsProxy tmsProxy;

    @Autowired
    private EsProxy esProxy;

    /**
     * @param uuid
     * @param code
     * @param type
     * @param addressInfo
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean modifyAddressInfo(String uuid, long code, int type, JSONObject addressInfo) throws Exception {
        ModifyInfoEnum modifyInfoEnum = ModifyInfoEnum.valueOf(type);
        List<OrderHead> orders = this.loadOrders(code, modifyInfoEnum.getLoadType());
        boolean isSuccess = this.validate(orders, uuid, type);
        if (!isSuccess) {
            return isSuccess;
        }

        for (OrderHead order : orders) {
            try {
                this.processAddressInfo(uuid, code, type, addressInfo, order);
            } catch (Exception e) {
                logger.error("[ModifyAddressInfo][" + uuid + "] orderCode:" + order.getOrderCode() + " error");
                throw e;
            }
        }

        for (OrderHead order : orders) {
            //TODO:分布式事务没有保证
            try {
                boolean result = this.notifyTms(type, uuid, code, order, addressInfo);

                if (!result) {
                    this.handleException(uuid, type, EC.SYSTEM_CALL_FAIL.exception("(调用TMS)"));
                }
            } catch (Exception e) {
                logger.error("[ModifyAddressInfo][" + uuid + "] orderCode:" + order.getOrderCode() + " error");
                this.handleException(uuid, type, e);
            }
        }

        return true;
    }

    /**
     * @param freightList
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean modifyFreight(List<FreightDto> freightList) throws BusinessException {

        try {

            for (FreightDto freightDto : freightList) {
                Long orderCode = freightDto.getOrderCode();
                BigDecimal freight = freightDto.getFreight();
                OrderHead orderHead = orderQueryService.findByCode(orderCode, false, false);
                if (null == orderHead) {
                    throw EC.UPDATE_NO_ORDER.exception("或订单已拆单");
                }

                OrderHead update = new OrderHead();
                update.setFreight(freight);
                OrderHead filter = new OrderHead();
                filter.setId(orderHead.getId());
                filter.setOrderCode(orderHead.getOrderCode());
                int ret = orderUpdateService.updateOrderHead(update, filter);
                if (ret <= 0) {
                    throw EC.UPDATE_DB_ERROR.exception();
                }
            }
        } catch (Exception e) {
            logger.error("[modifyFreight] error" + JSON.toJSONString(freightList), e);
            throw EC.UPDATE_DB_ERROR.exception();
        }

        return true;
    }

    /**
     * @param uuid
     * @param code
     * @param type
     * @param value
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean modifyExtInfo(String uuid, long code, int type, String value) throws Exception {
        ModifyInfoEnum modifyInfoEnum = ModifyInfoEnum.valueOf(type);
        List<OrderHead> orders = this.loadOrders(code, modifyInfoEnum.getLoadType());

        boolean isSuccess = this.validate(orders, uuid, type);
        if (!isSuccess) {
            return false;
        }
        try {
            for (OrderHead order : orders) {
                boolean result = this.processExtInfo(uuid, code, type, value, order);
                if (!result) {
                    this.handleException(uuid, type, EC.ERROR.exception("处理失败"));
                }
            }
        } catch (Exception e) {
            logger.error("[ModifyExtInfo][" + uuid + "] error");
            throw e;
        }

        return true;
    }

    /**
     * @param type
     * @param addressInfo
     * @return
     */
    public boolean checkAddressInfo(Integer type, JSONObject addressInfo) {
        if (type == ModifyInfoEnum.DELIVERY_TIME.getType()) {
            if (!StringUtils.hasText(addressInfo.getString(DELIVERY_TIME_MEMO))) {
                return false;
            }
        } else if (type == ModifyInfoEnum.TRANS_LIMIT_WHEELBARROW.getType()) {
            if (!StringUtils.hasText(addressInfo.getString(TRANS_LIMIT)) &&
                    !StringUtils.hasText(addressInfo.getString(WHEELBARROW))) {
                return false;
            }
        } else {
            return false;
        }

        return true;
    }

    /**
     * @param type
     * @param value
     * @return
     */
    public boolean checkExtInfo(Integer type, String value) {
        if (type == ModifyInfoEnum.IS_DELAY_PAYMENT.getType() || type == ModifyInfoEnum.IS_PAY.getType()) {
            if (value.equals(String.valueOf(YN.Y.getValue())) || value.equals(String.valueOf(YN.N.getValue()))) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }


    /**
     * 加载订单
     *
     * @param code
     * @param loadType
     * @return
     */
    private List<OrderHead> loadOrders(long code, int loadType) {
        List<OrderHead> orders = null;
        if (loadType == 1) {
            OrderHead orderHead = orderQueryService.findByCode(code, false, false);
            if (orderHead != null) {
                orders = new ArrayList<>();
                orders.add(orderHead);
            }
        }
        if (loadType == 2) {
            orders = orderQueryService.findListByAddressCodeAndStatusRange(code, OrderStatus.ORDER_NO_EDIT.getIndex(), OrderStatus.ORDER_WM_NEW.getIndex());
        }

        return orders;
    }

    /**
     * 验证订单
     *
     * @param orders
     * @param uuid
     * @param type
     * @return
     * @throws Exception
     */
    private boolean validate(List<OrderHead> orders, String uuid, int type) throws Exception {
        if (orders == null || orders.isEmpty()) {
            Exception exception = this.handleDataNullException(uuid, type);
            if (exception == null) {
                return true;
            } else {
                throw exception;
            }
        }

        return true;
    }

    /**
     * 处理地址信息
     *
     * @param uuid
     * @param code
     * @param type
     * @param addressInfo
     * @param orderHead
     * @return
     * @throws BusinessException
     * @throws IOException
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean processAddressInfo(String uuid, long code, int type, JSONObject addressInfo, OrderHead orderHead) throws BusinessException, IOException {
        logger.info("[ModifyAddressInfo][" + uuid + "] orderCode:" + orderHead.getOrderCode() + " start");

        int orderStatus = orderHead.getOrderStatus().intValue();
        if (orderStatus == OrderStatus.ORDER_COMMITED_OFC.getIndex() ||
                orderStatus == OrderStatus.ORDER_SORTING.getIndex() ||
                orderStatus == OrderStatus.ORDER_DELIVERY.getIndex() ||
                orderStatus == OrderStatus.ORDER_SIGN.getIndex() ||
                orderStatus == OrderStatus.ORDER_FINISH.getIndex() ||
                orderStatus == OrderStatus.ORDER_CANCEL_CUSTOMERSERVICE.getIndex() ||
                orderStatus == OrderStatus.ORDER_CANCEL_CUSTOMER.getIndex()) {
            throw EC.STATUS_NOT_OPERATE.exception();
        }

        JSONObject orderAddressInfo;
        if (StringUtils.hasText(orderHead.getAddressInfo())) {
            orderAddressInfo = JSONObject.parseObject(orderHead.getAddressInfo());
        } else {
            orderAddressInfo = new JSONObject();
        }

        log.info(orderHead.getOrderCode() + " ：订单的地址信息 " + orderAddressInfo.toJSONString());

        this.setAddressInfo(type, orderAddressInfo, addressInfo);
        OrderHead update = new OrderHead();
        update.setAddressInfo(orderAddressInfo.toJSONString());
        OrderHead filter = new OrderHead();
        filter.setId(orderHead.getId());
        filter.setOrderCode(orderHead.getOrderCode());
        filter.setOrderStatus(orderHead.getOrderStatus());
        int ret = orderUpdateService.updateOrderHead(update, filter);
        if (ret <= 0) {
            throw EC.UPDATE_DATA_FAIL.exception();
        }

        orderHead.setAddressInfo(orderAddressInfo.toJSONString());

        logger.info("[ModifyAddressInfo][{}] orderCode:{} end", uuid, orderHead.getOrderCode());

        return true;
    }

    /**
     * @param type
     * @param orderAddressInfo
     * @param addressInfo
     */
    private void setAddressInfo(int type, JSONObject orderAddressInfo, JSONObject addressInfo) {
        if (type == ModifyInfoEnum.DELIVERY_TIME.getType()) {

            JSONObject deliveryTimeMemo = null;
            if (orderAddressInfo.containsKey(DELIVERY_TIME_MEMO)) {
                deliveryTimeMemo = JSONObject.parseObject(orderAddressInfo.getString(DELIVERY_TIME_MEMO));
            }

            if (null == deliveryTimeMemo) {
                deliveryTimeMemo = new JSONObject();
            }

            JSONObject targetDeliveryTimeMemo = JSONObject.parseObject(addressInfo.getString(DELIVERY_TIME_MEMO));
            for (Map.Entry<String, Object> entry : targetDeliveryTimeMemo.entrySet()) {
                deliveryTimeMemo.put(entry.getKey(), entry.getValue());
            }

            orderAddressInfo.put(DELIVERY_TIME_MEMO, deliveryTimeMemo.toJSONString());
        }

        if (type == ModifyInfoEnum.TRANS_LIMIT_WHEELBARROW.getType()) {
            String transLimit = addressInfo.getString(TRANS_LIMIT);
            if (StringUtils.hasText(transLimit)) {
                orderAddressInfo.put(TRANS_LIMIT, transLimit);
            }

            String wheelbarrow = addressInfo.getString(WHEELBARROW);
            if (StringUtils.hasText(wheelbarrow)) {
                JSONObject deliveryTimeMemo = null;
                if (orderAddressInfo.containsKey(DELIVERY_TIME_MEMO)) {
                    deliveryTimeMemo = JSONObject.parseObject(orderAddressInfo.getString(DELIVERY_TIME_MEMO));
                }

                if (deliveryTimeMemo == null) {
                    deliveryTimeMemo = new JSONObject();
                }

                deliveryTimeMemo.put(WHEELBARROW, wheelbarrow);
                orderAddressInfo.put(DELIVERY_TIME_MEMO, deliveryTimeMemo.toJSONString());
            }
        }
    }

    /**
     * 处理订单扩展信息
     *
     * @param uuid
     * @param code
     * @param type
     * @param value
     * @param orderHead
     * @return
     * @throws BusinessException
     */
    @Transactional(rollbackFor = RuntimeException.class)
    boolean processExtInfo(String uuid, long code, int type, String value, OrderHead orderHead) throws BusinessException {
        logger.info("[ModifyExtInfo][{}] orderCode:{} start", uuid, orderHead.getOrderCode());

        JSONObject orderExtInfo;
        if (StringUtils.hasText(orderHead.getExt())) {
            orderExtInfo = JSONObject.parseObject(orderHead.getExt());
        } else {
            orderExtInfo = new JSONObject();
        }

        this.setExtInfo(type, orderExtInfo, value);

        OrderHead update = new OrderHead();
        update.setExt(orderExtInfo.toJSONString());
        OrderHead filter = new OrderHead();
        filter.setId(orderHead.getId());
        int ret = orderUpdateService.updateOrderHead(update, filter);
        if (ret <= 0) {
            throw EC.UPDATE_DATA_FAIL.exception();
        }

        orderHead.setExt(orderExtInfo.toJSONString());

        //更新Elasticsearch
        OrderHead esOrderHead = new OrderHead();
        esOrderHead.setOrderCode(orderHead.getOrderCode());
        esOrderHead.setExt(orderExtInfo.toJSONString());
        AsyncTaskHandler.executeAsyncTask(this.esProxy.newAsyncEditTask(EsProxy.transformToOrderHeadForEs(esOrderHead), uuid));

        logger.info("[ModifyExtInfo][{}] orderCode: {} end", uuid, orderHead.getOrderCode());

        return true;
    }

    /**
     * @param type
     * @param orderExtInfo
     * @param value
     */
    private void setExtInfo(int type, JSONObject orderExtInfo, String value) {
        if (type == ModifyInfoEnum.IS_DELAY_PAYMENT.getType()) {
            orderExtInfo.put(Constants.IS_DELAY_PAYMENT, value);
        }

        if (type == ModifyInfoEnum.IS_PAY.getType()) {
            orderExtInfo.put(Constants.IS_PAY, value);
        }
    }

    /**
     * 处理异常
     *
     * @param uuid
     * @param type
     * @return
     * @throws Exception
     */
    private Exception handleDataNullException(String uuid, int type) throws BusinessException {
        if (type == ModifyInfoEnum.DELIVERY_TIME.getType()
                || type == ModifyInfoEnum.IS_DELAY_PAYMENT.getType()
                || type == ModifyInfoEnum.IS_PAY.getType()) {
            return EC.DATA_IS_NULL.exception();
        }

        if (type == ModifyInfoEnum.TRANS_LIMIT_WHEELBARROW.getType()) {
            logger.warn(" [ModifyAddressInfo][{}][Don't have data]", uuid);
            return null;
        }

        return null;
    }

    /**
     * @param uuid
     * @param type
     * @param e
     * @throws Exception
     */
    private void handleException(String uuid, int type, Exception e) throws Exception {
        if (type == ModifyInfoEnum.DELIVERY_TIME.getType()
                || type == ModifyInfoEnum.IS_DELAY_PAYMENT.getType()
                || type == ModifyInfoEnum.IS_PAY.getType()) {
            throw e;
        }

        if (type == ModifyInfoEnum.TRANS_LIMIT_WHEELBARROW.getType()) {
            logger.error(" [ModifyAddressInfo][{}][Modify address info error, error message:{}]", uuid, e.getMessage());
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * 通知tms
     *
     * @param type
     * @param uuid
     * @param code
     * @param orderHead
     * @param addressInfo
     * @return
     * @throws IOException
     */
    private boolean notifyTms(int type, String uuid, long code, OrderHead orderHead, JSONObject addressInfo) throws IOException {
        boolean result = true;
        if (type == ModifyInfoEnum.DELIVERY_TIME.getType()) {
            result = this.changeDeliveryTime(uuid, code, orderHead, addressInfo);
        }

        if (type == ModifyInfoEnum.TRANS_LIMIT_WHEELBARROW.getType()) {
            result = this.changeTransLimitWheelbarrow(uuid, code, orderHead, addressInfo);
        }
        return result;
    }

    /**
     * @param uuid
     * @param code
     * @param orderHead
     * @param addressInfo
     * @return
     * @throws IOException
     */
    private boolean changeDeliveryTime(String uuid, long code, OrderHead orderHead, JSONObject addressInfo) throws IOException {
        boolean result = true;
        if (orderHead.getOrderStatus() >= OrderStatus.ORDER_WM_NEW.getIndex()) {
            JSONObject orderAddressInfo = JSONObject.parseObject(orderHead.getAddressInfo());
            HttpMessage httpMessage = tmsProxy.changeDeliveryTime(uuid, code, orderAddressInfo.getString(DELIVERY_TIME_MEMO),orderHead.getVenderId());
            result = TmsProxy.isSuccess(httpMessage.getMessage());
        }

        return result;
    }

    /**
     * @param uuid
     * @param code
     * @param orderHead
     * @param addressInfo
     * @return
     * @throws IOException
     */
    private boolean changeTransLimitWheelbarrow(String uuid, long code, OrderHead orderHead, JSONObject addressInfo) throws IOException {
        if (orderHead.getOrderStatus().intValue() >= OrderStatus.ORDER_WM_NEW.getIndex()) {
            if (StringUtils.hasText(addressInfo.getString(TRANS_LIMIT))) {
                tmsProxy.changeTransLimit(uuid, orderHead.getOrderCode(), addressInfo.getString(TRANS_LIMIT),orderHead.getVenderId());
            }
            if (StringUtils.hasText(addressInfo.getString(WHEELBARROW))) {
                JSONObject orderAddressInfo = JSONObject.parseObject(orderHead.getAddressInfo());
                tmsProxy.changeDeliveryTime(uuid, orderHead.getOrderCode(), orderAddressInfo.getString(DELIVERY_TIME_MEMO),orderHead.getVenderId());
            }
        }
        return true;
    }

    /**
     *
     */
    private enum ModifyInfoEnum {
        /**
         *
         */
        DELIVERY_TIME(1, 1),
        TRANS_LIMIT_WHEELBARROW(2, 2),
        IS_DELAY_PAYMENT(3, 1),
        IS_PAY(4, 1);

        private int type;
        private int loadType;

        ModifyInfoEnum(int type, int loadType) {
            this.type = type;
            this.loadType = loadType;
        }

        public static ModifyInfoEnum valueOf(int value) {
            for (ModifyInfoEnum item : ModifyInfoEnum.values()) {
                if (item.getType() == value) {
                    return item;
                }
            }
            return null;
        }

        public int getType() {
            return type;
        }

        public int getLoadType() {
            return loadType;
        }
    }

}
